/*
    Copyright (c) 2009 Pavol Malosek a.k.a. malo

    This file is part of ComComLib.

    ComComLib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    ComComLib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <string.h>
#include <iostream>
#include <iomanip>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <unistd.h>
#include <stdint.h>
#include <iomanip>
#include <time.h>
#include <sys/time.h>

#include <lcc/device.hpp>
#include <lcc/dlmath.hpp>

//#define LCC_DEVICE_DEBUG
//#define LCC_DEVICE_DEBUG_LEVEL 1

// level 1 = key behaviour
// level 2 = processing flow
// level 4 = infos

#ifndef LCC_DEVICE_DEBUG
#   define lcc_log(n, ...)  while (0)
#else
#   define lcc_log(n, ...)    do { if ((n) <= LCC_DEVICE_DEBUG_LEVEL) \
                                { printf (__VA_ARGS__);}} while (0)
#endif

//  Device codes table
lcc::device_codes_t device_codes [] = {
    {0x01,0x08,"R3120","Tint, H", 0x03, 0xf7, true}
};

lcc::device_codes_t lcc::get_codes (int type_, int model_)
{
    size_t codes_rows = sizeof (device_codes) / sizeof (device_codes_t);
    
    for (unsigned int i = 0; i < codes_rows; i++) {
        if (device_codes [i].type == type_ && device_codes [i].model == model_)
            return device_codes [i];
    }

    std::cerr << "Sorry your logger type: " << type_ << ", model: " << model_ 
        << " is not supported." << std::endl;
   
    std::cerr << "Please visit http://code.google.com/p/comcomlib/ for "
        "more info." << std::endl;

    assert (false);
}

lcc::device_t::device_t (void) :  status_word (0), record_ptr (0),
    service_memory (NULL), data_memory (NULL), tty_fd (-1), 
    poll_interval (0), next_poll (0)
{
    //  Memory cleanup.
    memset (tty_name, '\0', sizeof (tty_name));
    memset (&device_info, '\0', sizeof (device_info_t));

    //  Initialise online_record_mutex
    int rc = pthread_mutex_init (&online_record_mutex, NULL);
    assert (rc == 0);

    for (unsigned int i = 0; i < max_channels; i++) {
        online_record.values [i].input_type = in_off;
    }

}

//  Open tty.
int lcc::device_t::open_tty (void)
{
    //  If tty open just return fd.
    if (tty_fd != -1)
        return tty_fd;

    lcc_log (1, "Opening %s device, %s(%i)\n", tty_name, 
        __FILE__, __LINE__);

    struct termios new_tio;
    tty_fd = open (tty_name, O_RDWR | O_NOCTTY );
    if (tty_fd == -1 ) {
        lcc_log (1, "Could not open device %s, %s(%i)\n", tty_name, 
            __FILE__, __LINE__);
        return tty_fd;
    }
    
    // save current port settings
    tcgetattr (tty_fd, &old_tio); 

    memset (&new_tio, '\0', sizeof (termios));
    new_tio.c_cflag = B38400 | CS8 | CLOCAL | CREAD;
    new_tio.c_iflag = IGNPAR;
    new_tio.c_oflag = 0;

    // set input mode (non-canonical, no echo,...)
    new_tio.c_lflag = 0;

    
    tcflush (tty_fd, TCIFLUSH);
    tcsetattr(tty_fd, TCSANOW,&new_tio);

    //  Reset DTR & RTS
    int status;
    //  Get current status.
    ioctl(tty_fd, TIOCMGET, &status);

    status |= TIOCM_DTR;
    status |= TIOCM_RTS;

    ioctl(tty_fd, TIOCMSET, &status); 

    lcc_log (1, "Device %s opened as fd %i, %s(%i)\n", tty_name, tty_fd, 
        __FILE__, __LINE__);

    return tty_fd;
}

void lcc::device_t::close_tty (bool keep_tty_open_)
{
    assert (tty_fd != -1);

    //  Actually we do not want to close device.
    if (keep_tty_open_)
        return;

    lcc_log (1, "Closing device %s fd %i, %s(%i)\n", tty_name, tty_fd,
        __FILE__, __LINE__);

    tcflush (tty_fd, TCIFLUSH);
    tcsetattr (tty_fd, TCSANOW, &old_tio);

    //  Close tty device
    close (tty_fd);

    //  Mark tty_fd as unused.
    tty_fd = -1;
}

ssize_t lcc::device_t::write_read_tty (const void *write_data_, 
    size_t write_size_, void *read_buff_, size_t read_size_)
{

    ssize_t nbytes = 0;
    bool second_pass = false;

    while (true) {
        nbytes = write_tty (write_data_, write_size_);
        assert (nbytes == (ssize_t)write_size_);

        //  Wait for a while for response.
        usleep (1000);

        nbytes = read_tty (read_buff_, read_size_);
        assert (nbytes == (ssize_t)read_size_ || nbytes == 0);

        if (nbytes == 0 && second_pass)
            break;

        if (nbytes == 0) {
            second_pass = true;
            //  Accordig protocol we should wait 1s. 
            sleep (1);
            continue;
        }

        break;
    }

    return nbytes;
}

ssize_t lcc::device_t::write_tty (const void *data_, size_t size_)
{
    lcc_log (1, "Writting %i bytes to %s, %s(%i)\n", (int)size_, tty_name,
        __FILE__, __LINE__);

#if defined LCC_DEVICE_DEBUG
    if (LCC_DEVICE_DEBUG_LEVEL >= 2) {
        for (unsigned int i = 0; i < size_; i++) {
            printf ("[0x%x]", *((unsigned char*)data_ + i));
        }
        printf ("\n");
    }
#endif

    ssize_t nbytes = write (tty_fd, data_, size_);

    assert (nbytes = size_);

    return nbytes;
}

ssize_t lcc::device_t::read_tty (void *buff_, size_t size_, 
    unsigned int timeout_)
{
    //  Have to set timer and how many charactes we need to read.
    struct termios new_tio;

    size_t nbytes_read = 0; 
    unsigned char *buff_write_location = (unsigned char*)buff_;

    // Load current port settings.
    tcgetattr (tty_fd, &new_tio); 

    //  Set the timeout.
    new_tio.c_cc [VTIME] = timeout_;
    new_tio.c_cc [VMIN] = 0;

    tcsetattr (tty_fd, TCSANOW,&new_tio);

    while (nbytes_read != size_) {

        ssize_t nbytes = 
            read (tty_fd, buff_write_location, size_ - nbytes_read);

        assert (nbytes != -1);

        lcc_log (4, "Read %i Bytes from %s, %s(%i)\n", (int)nbytes, tty_name, 
            __FILE__, __LINE__);
        
        nbytes_read += nbytes;
        buff_write_location += nbytes;

        if (!nbytes){
            nbytes_read = 0;
            break;
        }
    }
   
    lcc_log (1, "Read %i Bytes from %s, %s(%i)\n", (int)nbytes_read, tty_name, 
        __FILE__, __LINE__);

#if defined LCC_DEVICE_DEBUG
    if (nbytes_read > 0 && LCC_DEVICE_DEBUG_LEVEL >= 2) {
        for (unsigned int i = 0; i < nbytes_read; i++) {
            printf ("[0x%2.2x]", *((unsigned char*)buff_ + i));
        }
        printf ("\n");
    }
#endif

    return nbytes_read;
}

ssize_t lcc::device_t::read_256_EE (void *dest_, uint16_t src_addr_)
{
    return read_EE (dest_, src_addr_, 0xff);
}

ssize_t lcc::device_t::read_EE (void *dest_, uint16_t src_addr_, size_t size_)
{
    
    unsigned char mem_read_command [5];
    mem_read_command [0] = 0x03;
    mem_read_command [1] = (unsigned char) (((src_addr_) >> 8) & 0xff);
    mem_read_command [2] = (unsigned char) (src_addr_ & 0xff);
    mem_read_command [3] = size_;
    mem_read_command [4] = checksum_range (mem_read_command , 4);

    //  Logger should return 0x03 status data checksum 
    ssize_t to_read = sizeof (uint16_t) + size_ + sizeof (uint8_t) + 1;

    unsigned char *read_buff = new unsigned char [to_read];

    ssize_t nbytes = write_read_tty (mem_read_command, 
        sizeof (mem_read_command), read_buff, to_read);

    if (nbytes > 0) {
        
        assert (nbytes == to_read);

        assert (checksum_range ((unsigned char*)read_buff, to_read - 1) == 
            ((unsigned char*)read_buff) [to_read - 1]);

        memcpy ((unsigned char*)dest_, read_buff + sizeof (uint16_t), 
            size_ + 1);

        nbytes = size_ + 1;
    }

    delete [] read_buff;

    return nbytes;
}

ssize_t lcc::device_t::read_status_word (void)
{

    unsigned char status_word_read_command [5];
    status_word_read_command [0] = 0x07;
    status_word_read_command [1] = 0xff;
    status_word_read_command [2] = 0xff;
    status_word_read_command [3] = 0xff;
    status_word_read_command [4] = 0x04;

    //  Logger should return 0x07 status stat_H stat_LO checksum 
    ssize_t to_read = sizeof (uint16_t) + sizeof (uint16_t) + sizeof (uint8_t);

    unsigned char *read_buff = new unsigned char [to_read];

    ssize_t nbytes = write_read_tty (status_word_read_command, 
        sizeof (status_word_read_command), read_buff, to_read);

    if (nbytes > 0) {
         
        assert (nbytes = to_read);

        assert (checksum_range ((unsigned char*)read_buff, to_read - 1) == 
            ((unsigned char*)read_buff) [to_read - 1]);
        
        memset (&status_word, '\0', sizeof (uint16_t));
        status_word =  read_buff [2] << 8 | read_buff [3]; 
        
        lcc_log (1, "Status word 0x%x, %s(%i)\n", status_word, 
            __FILE__, __LINE__);

        nbytes = sizeof (uint16_t);
    }

    delete [] read_buff;

    return nbytes;
}

ssize_t lcc::device_t::read_record_ptr (void)
{

    unsigned char record_ptr_read_command [5];
    record_ptr_read_command [0] = 0x08;
    record_ptr_read_command [1] = 0xff;
    record_ptr_read_command [2] = 0xff;
    record_ptr_read_command [3] = 0xff;
    record_ptr_read_command [4] = 0x05;

    //  Logger should return 0x08 status addr_H addr_LO checksum 
    ssize_t to_read = sizeof (uint16_t) + sizeof (uint16_t) + sizeof (uint8_t);

    unsigned char *read_buff = new unsigned char [to_read];

    ssize_t nbytes = write_read_tty (record_ptr_read_command, 
        sizeof (record_ptr_read_command), read_buff, to_read);

    if (nbytes > 0) {
         
        assert (nbytes = to_read);

        assert (checksum_range ((unsigned char*)read_buff, to_read - 1) == 
            ((unsigned char*)read_buff) [to_read - 1]);
        
        memset (&record_ptr, '\0', sizeof (uint16_t));
        record_ptr =  read_buff [2] << 8 | read_buff [3]; 
        
        lcc_log (1, "Record ptr 0x%x, %s(%i)\n", record_ptr, 
            __FILE__, __LINE__);

        nbytes = sizeof (uint16_t);
    }

    delete [] read_buff;

    return nbytes;
}

bool lcc::device_t::read_ADC4 (void)
{

    //  We need device_info informations.
    if (!service_memory)
        return false;

    //  ADC value of channel 1
    uint16_t ADC_ch_1 = 0;

    //  ADC value of channel 2
    uint16_t ADC_ch_2 = 0;

    unsigned char ADC_read_command [5];
    ADC_read_command [0] = 0x0a;
    ADC_read_command [1] = 0xff;
    ADC_read_command [2] = 0xff;
    ADC_read_command [3] = 0xff;
    ADC_read_command [4] = 0x07;

    //  Logger should return 0x0a, status, 10B, checkum 
    ssize_t to_read = sizeof (uint16_t) + sizeof (uint8_t) * 10 + sizeof (uint8_t);

    unsigned char *read_buff = new unsigned char [to_read];

    ssize_t nbytes = write_read_tty (ADC_read_command, 
        sizeof (ADC_read_command), read_buff, to_read);

    if (nbytes > 0) {
         
        assert (nbytes = to_read);

        assert (checksum_range ((unsigned char*)read_buff, to_read - 1) == 
            ((unsigned char*)read_buff) [to_read - 1]);
        
        ADC_ch_1 = read_buff [2] << 8 | read_buff [3];

        ADC_ch_2 = read_buff [4] << 8 | read_buff [5];
        
        lcc_log (1, "ADC channel 1 %i, %s(%i)\n", ADC_ch_1, 
            __FILE__, __LINE__);

        lcc_log (1, "ADC channel 2 %i, %s(%i)\n", ADC_ch_2, 
            __FILE__, __LINE__);

        //  Store ADC values into online_record and calculate real values.
        int rc = pthread_mutex_lock (&online_record_mutex);
        assert (rc == 0);

        online_record.stamp = lcc::time_now ();

        online_record.values [0].adc_value = ADC_ch_1;
        online_record.values [0].input_type = device_info.input_type [0];

        online_record.values [1].adc_value = ADC_ch_2;
        online_record.values [1].input_type = device_info.input_type [1];

        online_record.values [2].input_type = in_off;
        online_record.values [3].input_type = in_off;

        adc_to_real_value (&device_info, &online_record);

        rc = pthread_mutex_unlock (&online_record_mutex);
        assert (rc == 0);
    }

    delete [] read_buff;

    return true;
}

bool lcc::device_t::bogus_command (void)
{
    //  Reading buffer.
    unsigned char read_buff [3];

    //  Prepare bogus command RTC read with wrong checksum.
    unsigned char bogus_command [5];

    bogus_command [0] = 0x01;
    bogus_command [1] = 0xff;
    bogus_command [2] = 0xff;
    bogus_command [3] = 0xff;
    // Wrong checksum.
    bogus_command [4] = 0x00;
    
    ssize_t nbytes = write_read_tty (bogus_command, sizeof (bogus_command),
        read_buff, sizeof (read_buff));

    //  Device is not responding.
    if (nbytes == 0)
        return false;

    assert (nbytes ==  sizeof (read_buff));

    //  Check response can be checksum error or no response in 9.1ms
    if ((read_buff [0] != 0x1 || read_buff [1] != 0x2 || 
          read_buff [2] != 0x3) && (read_buff [0] != 0x0 || 
          read_buff [1] != 0x2 || read_buff [2] != 0x2)) {
        return false;
    }
    
    return true;
}

bool lcc::device_t::update_device_info (void)
{

    lcc_log (1, "Updating device info, %s(%i)\n", __FILE__, __LINE__);

    memset (&device_info, '\0', sizeof (device_info_t)); 
    
    if (!service_memory)
        return false;

    //  Device type
    //
    //  0x04h type
    //      type - 0x1h Rxxxx
    //      type - 0x2h Sxxxx
    unsigned char type = service_memory [0x04];
    device_info.type = type;

    //  Device model
    //  0x05h model
    unsigned char model = service_memory [0x05];
    device_info.model = model;

    lcc_log (1, "Logger type 0x%xh, model 0x%xh, %s(%i)\n", 
        device_info.type, device_info.model, __FILE__, __LINE__);

    //  Get device codes.
    device_codes_t codes = get_codes (device_info.type, 
        device_info.model);

    lcc_log (1, "Logger name %s, measuring %s, %s(%i)\n",
        codes.name.c_str(), codes.measured_values.c_str (), 
        __FILE__, __LINE__);

    //  Logging interval
    //
    //  0x00h per_l
    //  0x01h per_h
    //  per_h bit 0 - 10 sec
    //        bit 1 - 1 min
    //        bit 2 - 1 hod
    unsigned char per_l = service_memory [0x00];
    unsigned char per_h = service_memory [0x01];

    unsigned int time_mult = 0;

    if (per_h & 0x1)
        time_mult = 10;
    else if (per_h & 0x2)
        time_mult = 60;
    else if (per_h & 0x4)
        time_mult = 60 * 60;
    else
        assert (false);

    device_info.per = per_l * time_mult;

    lcc_log (4, "Logging interval: per_l %i, per_h 0x%x, interval %i sec,"\
        "%s(%i)\n", per_l, per_h, device_info.per, __FILE__, __LINE__);
   
    //  Cyclic / noncyclic logging mode
    //
    //  0x02h lincyk
    //  lincyk 0x00h - cyclic logging mode
    //         0x01h - noncyclic logging more
    //         other - noncyclic mode
    unsigned char lincyk = service_memory [0x02];

    if (lincyk == 0)
        device_info.lincyk = true;
    else
        device_info.lincyk = false;

    lcc_log (4, "Cyclic mode: lincyk 0x%xh, %s, %s(%i)\n", lincyk, 
        device_info.lincyk ? "true" : "false", __FILE__, __LINE__);
   
    //  Active channels
    //
    //  0x03h act_channels
    //  act_channels bit 0 - channel 1 on
    //               bit 1 - channel 2 on
    //               bit 2 - channel 3 on
    //               bit 3 - channel 4 on

    device_info.act_channels = service_memory [0x03];
    device_info.act_channels &= codes.channel_mask;

    lcc_log (4, "Active channels: act_channels mask 0x%xh, %s(%i)\n", 
        device_info.act_channels, __FILE__, __LINE__);
    
    //  Number of channels active
    device_info.act_channels_count = act_bits (device_info.act_channels);

    lcc_log (4, "Active channels count: act_channels_count %i, %s(%i)\n", 
        device_info.act_channels_count, __FILE__, __LINE__);


    //  Runtime parameters
    //
    //  0x06h     bit 0 - logger switched ON 
    //            bit 1 - dealayed start
    //            bit 2 - logging start by magnet enabled
    //            bit 3 - logging end by magnet enabled
    //            bit 4 - logging only outside of alar.limits 
    device_info.dev_on = service_memory [0x06] & 0x01;
    device_info.dly_on = service_memory [0x06] & 0x02;
    device_info.mag_on = service_memory [0x06] & 0x04;
    device_info.mag_off = service_memory [0x06] & 0x08;
    device_info.mimo_al = service_memory [0x06] & 0x10;

    lcc_log (4, "Runtime parameters 0x%xh, %s(%i)\n", service_memory [0x06],
        __FILE__, __LINE__);

    lcc_log (4, "Logger switched ON %s, %s(%i)\n", 
        device_info.dev_on ? "true" : "false", __FILE__, __LINE__);

    lcc_log (4, "Delayed start %s, %s(%i)\n", 
        device_info.dly_on ? "true" : "false", __FILE__, __LINE__);

    lcc_log (4, "Logging start by magnet enabled %s, %s(%i)\n", 
        device_info.mag_on ? "true" : "false", __FILE__, __LINE__);

    lcc_log (4, "Logging end by magnet enabled %s, %s(%i)\n", 
        device_info.mag_off ? "true" : "false", __FILE__, __LINE__);

    lcc_log (4, "Logging only outside of alarm limits %s, %s(%i)\n", 
        device_info.mimo_al ? "true" : "false", __FILE__, __LINE__);

    //  Konfig
    //
    //  0x07h    bit 7 - linearisation of AD converter is switched ON
    unsigned char konfig = service_memory [0x07];

    if (codes.konfig_and)
        konfig &= codes.konfig_mask;
    else
        konfig |= codes.konfig_mask;

    device_info.adc_lin = konfig & 0x80;

    lcc_log (4, "Konfig parameters 0x%xh, %s(%i)\n", service_memory [0x07],
        __FILE__, __LINE__);

    lcc_log (4, "ADC linearisation switched ON %s, %s(%i)\n", 
        device_info.adc_lin ? "true" : "false", __FILE__, __LINE__);

    //  Inputs configuration (max 4 inputs)
    //
    //  0x08h
    for (unsigned int i = 0; i < max_channels; i++) {
        unsigned char in_k = service_memory [0x08 + i];
        input_type_t in_type = in_off;
        
        //  Need only low byte.
        in_k &= 0x0f;

        if (in_k == 0x00)
            in_type = in_temp_ni;
        else if (in_k == 0x01)
            in_type = in_temp_pt;
        else if (in_k == 0x02)
            in_type = in_hum;
        else if (in_k == 0x03)
            in_type = in_u_i;
        else if (in_k == 0x07)
            in_type = in_bin;
        else if (in_k == 0x08)
            in_type = in_count_16;
        else if (in_k == 0x09)
            in_type = in_count_32;
        else if (in_k == 0x0f)
            in_type = in_off;

        device_info.input_type [i] = in_type;

        lcc_log (4, "Input %i, config 0x%x, type \'%s\', %s(%i)\n", i, in_k,
            input_type_to_string (device_info.input_type [i]),
            __FILE__, __LINE__);
    }

    //  Constants for measured values calculation.
    //  1. channel
    //   0x20h k_1_arg
    //   0x24h q_1_arg
    //  2. channel
    //   0x28h k_2_arg
    //   0x2ch q_2_arg
    //  3. channel
    //   0x30h k_3_arg
    //   0x34h q_3_arg
    //  4. channel
    //  0x38h k_4_arg
    //  0x3ch q_4_arg
    //
    //  1. channel
    //   0x70h k_1_usr
    //   0x74h q_1_usr
    //  2. channel
    //   0x78h k_2_usr
    //   0x7ch q_2_usr
    //  3. channel
    //   0x80h k_3_usr
    //   0x84h q_3_usr
    //  4. channel
    //   0x88h k_4_usr
    //   0x8ch q_4_usr

    unsigned int math_arg_base_addr = 0x20;
    unsigned int math_usr_base_addr = 0x70;

    for (unsigned int i = 0; i < max_channels; i++) {
    
        uint8_t k_i_arg_exp = service_memory [math_arg_base_addr + i * 8];
        uint8_t k_i_arg_b0 = service_memory [math_arg_base_addr + 1 + i * 8];
        uint8_t k_i_arg_b1 = service_memory [math_arg_base_addr + 2 + i * 8];
        uint8_t k_i_arg_b2 = service_memory [math_arg_base_addr + 3 + i * 8];

        float k_arg = dot_float_to_float (k_i_arg_exp, k_i_arg_b0, 
            k_i_arg_b1, k_i_arg_b2);

        uint8_t q_i_arg_exp = service_memory [math_arg_base_addr + 4 + i * 8];
        uint8_t q_i_arg_b0 = 
            service_memory [math_arg_base_addr + 4 + 1 + i * 8];
        uint8_t q_i_arg_b1 = 
            service_memory [math_arg_base_addr + 4 + 2 + i * 8];
        uint8_t q_i_arg_b2 = 
            service_memory [math_arg_base_addr + 4 + 3 + i * 8];

        float q_arg = dot_float_to_float (q_i_arg_exp, q_i_arg_b0, 
            q_i_arg_b1, q_i_arg_b2);

        uint8_t k_i_usr_exp = service_memory [math_usr_base_addr + i * 8];
        uint8_t k_i_usr_b0 = service_memory [math_usr_base_addr + 1 + i * 8];
        uint8_t k_i_usr_b1 = service_memory [math_usr_base_addr + 2 + i * 8];
        uint8_t k_i_usr_b2 = service_memory [math_usr_base_addr + 3 + i * 8];

        float k_usr = dot_float_to_float (k_i_usr_exp, k_i_usr_b0, 
            k_i_usr_b1, k_i_usr_b2);

        uint8_t q_i_usr_exp = service_memory [math_usr_base_addr + 4 + i * 8];
        uint8_t q_i_usr_b0 = 
            service_memory [math_usr_base_addr + 4 + 1 + i * 8];
        uint8_t q_i_usr_b1 = 
            service_memory [math_usr_base_addr + 4 + 2 + i * 8];
        uint8_t q_i_usr_b2 = 
            service_memory [math_usr_base_addr + 4 + 3 + i * 8];

        float q_usr = dot_float_to_float (q_i_usr_exp, q_i_usr_b0, 
            q_i_usr_b1, q_i_usr_b2);

        device_info.calc_constants [i].k_n_arg = k_arg;
        device_info.calc_constants [i].q_n_arg = q_arg;
        device_info.calc_constants [i].k_n_usr = k_usr;
        device_info.calc_constants [i].q_n_usr = q_usr;
    }

    //  Get data memory size from status word.
    if (status_word & 0x20) {
        device_info.data_memory_size = 65024;
    } else {
        device_info.data_memory_size = 32256;
    }

    lcc_log (4, "Data memory size %iB, %s(%i)\n", 
        (int)device_info.data_memory_size, __FILE__, __LINE__);

    //  data memory ower 90% full
    //  2. bit from stat_H
    device_info.EE_90 = status_word & 0x400;

    //  data memory is full
    //  0. bit from stat_L
    device_info.EE_full = status_word & 0x01;

    //  logging in progress
    device_info.rec_run = status_word & 0x80;

    //  record_ptr
    device_info.record_ptr = record_ptr;

    return true;
}

bool lcc::device_t::get_device_info (lcc::device_info_t *device_info_)
{
    if (!service_memory)
        return false;

    *device_info_ = device_info;

    return true;
}

void lcc::device_t::add_records (std::list <record_t> &records_)
{
    
    //  Calculate real values from adc_values. 
    adc_to_real_values (&device_info, records_);

#ifdef LCC_DEVICE_DEBUG
    std::list<record_t>::iterator it_dbg = records_.begin();
    
    while (LCC_DEVICE_DEBUG_LEVEL > 2 && it_dbg != records_.end()) {
        print_record (&*it_dbg);
        it_dbg++;
    }
#endif

    //  Add records into object's list.
    std::list<record_t>::iterator it = records.end();
    records.insert (it, records_.begin (), records_.end ());

    lcc_log (4, "Have %i records (%i new) in device, %s(%i)\n", 
        (int)records.size (), (int)records_.size (), __FILE__, __LINE__);
}

const char *lcc::input_type_to_string (input_type_t input_type_)
{
    
    switch (input_type_) {
        case in_temp_ni:
            return "tNi1000/6180";
        case in_temp_pt:
            return "tPt1000/3850";
        case in_hum:
            return "hum";
        case in_u_i:
            return "U/I";
        case in_bin:
            return "binary";
        case in_count_16:
            return "counter 16b";
        case in_count_32:
            return "counter 32b";
        case in_off:
            return "off - not measuring";
        default:
            return "unknown";
    }
}

void lcc::print_record (lcc::record_t *record_)
{
    //  0 is not valid stamp.
    if (!record_->stamp)
        return;

    struct tm *tm = localtime ((time_t*)&record_->stamp);
   
    std::cout << record_->stamp << ";";
    std::cout.width (2);
    std::cout.fill ('0');
    std::cout << tm->tm_hour;
    std::cout << ":";
    std::cout.width (2);
    std::cout.fill ('0');
    std::cout << tm->tm_min;
    std::cout << ":";
    std::cout.width (2);
    std::cout.fill ('0');
    std::cout << tm->tm_sec;
    std::cout << " ";
    std::cout << tm->tm_mday << "." << tm->tm_mon + 1 << "."
        << tm->tm_year + 1900;

    for (unsigned int i = 0; i < max_channels; i++) {
        if (record_->values [i].input_type == in_off)
            continue;

        std::cout << ";" << input_type_to_string (record_->values [i].input_type) 
            << ";";

//        std::cout << record_->values [i].adc_value << ";";

        std::cout << std::fixed << std::setprecision (1) 
            << record_->values [i].value;
    }
    
    std::cout << std::endl;

}

std::list<lcc::record_t> lcc::device_t::get_records (void)
{
    return records;
}

lcc::record_t lcc::device_t::get_online_record (void)
{

    record_t record_to_return;

    int rc = pthread_mutex_lock (&online_record_mutex);
    assert (rc ==0);

    record_to_return = online_record;

    rc = pthread_mutex_unlock (&online_record_mutex);
    assert (rc == 0);

    return record_to_return;
}

void lcc::print_device_info (device_info_t *device_info_)
{

    printf ("Logger type: 0x%xh, model: 0x%xh.\n", device_info_->type, 
        device_info_->model);

    //  Get device codes.
    device_codes_t codes = get_codes (device_info_->type, 
        device_info_->model);

    printf ("Logger name: %s, measuring: %s.\n", codes.name.c_str(),
        codes.measured_values.c_str ());

    printf ("Logging interval: %i sec.\n", device_info_->per);
   
    printf ("Cyclic mode: %s.\n", device_info_->lincyk ? "true" : "false");
   
    printf ("Active channels: act_channels mask: 0x%xh.\n", 
        device_info_->act_channels);
    
    printf ("Active channels count: act_channels_count: %i.\n", 
        device_info_->act_channels_count);

    printf ("Logger switched ON: %s.\n", 
        device_info_->dev_on ? "true" : "false");

    printf ("Delayed start: %s.\n", device_info_->dly_on ? "true" : "false");

    printf ("Logging start by magnet enabled: %s.\n", 
        device_info_->mag_on ? "true" : "false");

    printf ("Logging end by magnet enabled: %s.\n", 
        device_info_->mag_off ? "true" : "false");

    printf ("Logging only outside of alarm limits: %s.\n",
        device_info_->mimo_al ? "true" : "false");

    printf ("ADC linearisation switched ON: %s.\n", 
        device_info_->adc_lin ? "true" : "false");

    //  Inputs configuration (max 4 inputs)
    //
    //  0x08h
    for (unsigned int i = 0; i < max_channels; i++) {

        printf ("Input %i,  type: \'%s\'.\n", i, 
            input_type_to_string (device_info_->input_type [i]));
    }

    //  Constants for measured values calculation.
    for (unsigned int i = 0; i < max_channels; i++) {
    
        printf ("k_%i_arg: %.3f", i + 1, 
            device_info_->calc_constants [i].k_n_arg);
        printf ("; q_%i_arg: %.3f", i + 1, 
            device_info_->calc_constants [i].q_n_arg);
        printf ("; k_%i_usr: %.3f", i + 1, 
            device_info_->calc_constants [i].k_n_usr);
        printf ("; q_%i_usr: %.3f\n", i + 1, 
            device_info_->calc_constants [i].q_n_usr);
    }

    printf ("Data memory size: %iB.\n", (int)device_info_->data_memory_size);

    printf ("Record pointer 0x%x,\n", device_info_->record_ptr);

    printf ("Data memory ower 90%% full: %s.\n",
        device_info_->EE_90 ? "true" : "false");

    printf ("Data memory full: %s.\n",
        device_info_->EE_full ? "true" : "false");

    printf ("Recording: %s.\n",
        device_info_->rec_run ? "true" : "false");

    if (device_info_->EE_full) {
        printf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
        printf ("!!!!!!!!!! Logger memory full !!!!!!!!!!!\n");
        printf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
    }

}

lcc::logger_command_status_t 
    lcc::device_t::set_online_interval (size_t interval_)
{
    //  Set measure interval.
    poll_interval = interval_;

    //  Schedule next poll.
    next_poll = lcc::time_now ();

    return status_ok;
}

bool lcc::device_t::is_next_poll (uint64_t now_)
{
    //  Not online measuring.
    if (!poll_interval)
        return false;

    if (now_ >= next_poll) {
        next_poll = lcc::time_now () + poll_interval;
        return true;
    }
    
    return false;
}

uint64_t lcc::time_now (void)
{
    struct timeval tv;
    int rc = gettimeofday (&tv, NULL);
    assert (rc == 0);
    return tv.tv_sec;
}

bool lcc::records_sort_fcn (record_t r_a_, record_t r_b_)
{
    return r_a_.stamp < r_b_.stamp;
}

